﻿using System.IO;
using Duellum.Core;
using Duellum.Core.Types;
using JpLabs.Geometry;
using DuelCoreTypes = Duellum.Core.DuelCoreTypes;
using System.Collections.Generic;
using System;

namespace Duellum.Map
{
	internal static class MapLegacyReader
	{
		public const string MapFileExtension = ".MAP";

		private static IDictionary<int,string> ContentToTypeIdDict;
		
		static MapLegacyReader()
		{
			ContentToTypeIdDict = new Dictionary<int,string>(60);
			
			ContentToTypeIdDict.Add(0, null);
			ContentToTypeIdDict.Add(1,	DuelCoreTypes.Wall.Wooden);
			ContentToTypeIdDict.Add(5,	DuelCoreTypes.Wall.Brick);
			ContentToTypeIdDict.Add(10,	DuelCoreTypes.Wall.SecretBrickDoor);//deprecated code for editor mode
			ContentToTypeIdDict.Add(9,	DuelCoreTypes.Wall.SecretBrickDoor);//| CONT_FLAG_PORTA | CONT_PORTA_SECRETA;break;
			ContentToTypeIdDict.Add(34,	DuelCoreTypes.Wall.Glass);
			ContentToTypeIdDict.Add(44,	DuelCoreTypes.Wall.Fence);
			ContentToTypeIdDict.Add(47,	DuelCoreTypes.Wall.Stone);
			ContentToTypeIdDict.Add(48,	DuelCoreTypes.Wall.SteelDoor);//| CONT_FLAG_PORTA | CONT_PORTA_TRANCADA;chao = cChao::CHAO_PEDRA;break;
			ContentToTypeIdDict.Add(50,	DuelCoreTypes.Wall.SteelDoor);//| CONT_FLAG_PORTA | CONT_PORTA_TRANCADA | CONT_FLAG_PORTA_ABERTA;chao = cChao::CHAO_PEDRA;break;
				
			ContentToTypeIdDict.Add(54,	DuelCoreTypes.Transitional.Random_ActivatedLandMine);
			ContentToTypeIdDict.Add(43,	DuelCoreTypes.Transitional.Random_BreakingGround);
				
			ContentToTypeIdDict.Add(24,	DuelCoreTypes.Ground.Breaking);//deprecated code for editor mode
			ContentToTypeIdDict.Add(25,	DuelCoreTypes.Ground.Breaking);
			ContentToTypeIdDict.Add(26, null); //No Floor
				
			ContentToTypeIdDict.Add(40,	DuelCoreTypes.Area.Fire); //TODO: Everlasting Fire
			ContentToTypeIdDict.Add(31,	DuelCoreTypes.Area.Fire);
			ContentToTypeIdDict.Add(36,	DuelCoreTypes.Area.Smoke); //TODO: Darkness
			ContentToTypeIdDict.Add(22,	DuelCoreTypes.Area.Smoke);
			ContentToTypeIdDict.Add(35,	DuelCoreTypes.Area.Teleport);
			ContentToTypeIdDict.Add(53,	DuelCoreTypes.Area.ElevatorDownOnly);
			ContentToTypeIdDict.Add(20,	DuelCoreTypes.Area.ElevatorUpDown);
			ContentToTypeIdDict.Add(52,	DuelCoreTypes.Area.ElevatorUpOnly);
				
			ContentToTypeIdDict.Add(30,	DuelCoreTypes.MapObj.Bomb);
			ContentToTypeIdDict.Add(16,	DuelCoreTypes.MapObj.Corpse);
			ContentToTypeIdDict.Add(46,	DuelCoreTypes.MapObj.CovertVest);
			ContentToTypeIdDict.Add(32,	DuelCoreTypes.MapObj.ExplosiveBarrel);
			ContentToTypeIdDict.Add(13,	DuelCoreTypes.MapObj.Grenade);
			ContentToTypeIdDict.Add(14,	DuelCoreTypes.MapObj.Grenade_Activated);
			ContentToTypeIdDict.Add(51,	DuelCoreTypes.MapObj.HolographicProjector);
			ContentToTypeIdDict.Add(29,	DuelCoreTypes.MapObj.InfraRedGoggles);
			ContentToTypeIdDict.Add(28,	DuelCoreTypes.MapObj.InvisibilityHelmet);
			ContentToTypeIdDict.Add(49,	DuelCoreTypes.MapObj.Key);
			ContentToTypeIdDict.Add(7,	DuelCoreTypes.MapObj.Landmine);
			ContentToTypeIdDict.Add(33,	DuelCoreTypes.MapObj.LandMine_Activated);//deprecated code for editor mode
			ContentToTypeIdDict.Add(6,	DuelCoreTypes.MapObj.LandMine_Activated);
			ContentToTypeIdDict.Add(15,	DuelCoreTypes.MapObj.MedicineKit);
			ContentToTypeIdDict.Add(59,	DuelCoreTypes.MapObj.Motorcycle);
			ContentToTypeIdDict.Add(45,	DuelCoreTypes.MapObj.Parasite);
			ContentToTypeIdDict.Add(56,	DuelCoreTypes.MapObj.ProximityDetector);
			ContentToTypeIdDict.Add(39,	DuelCoreTypes.MapObj.RandomObjBox);
			ContentToTypeIdDict.Add(58,	DuelCoreTypes.MapObj.RandomWeaponBox);
				
			ContentToTypeIdDict.Add(41,	DuelCoreTypes.Gun.BlasterLauncher);
			ContentToTypeIdDict.Add(57,	DuelCoreTypes.Gun.Blunderbuss);
			ContentToTypeIdDict.Add(21,	DuelCoreTypes.Gun.Flamethrower);
			ContentToTypeIdDict.Add(55,	DuelCoreTypes.Gun.Machinegun);
			ContentToTypeIdDict.Add(19,	DuelCoreTypes.Gun.Minigun);
			ContentToTypeIdDict.Add(37,	DuelCoreTypes.Gun.ParalizerGun);
			ContentToTypeIdDict.Add(23,	DuelCoreTypes.Gun.Revolver);
			ContentToTypeIdDict.Add(12,	DuelCoreTypes.Gun.Poisongun);
			ContentToTypeIdDict.Add(27,	DuelCoreTypes.Gun.RicochetGun);
			ContentToTypeIdDict.Add(11,	DuelCoreTypes.Gun.Rifle);
			ContentToTypeIdDict.Add(8,	DuelCoreTypes.Gun.RocketLauncher);
			ContentToTypeIdDict.Add(3,	DuelCoreTypes.Gun.Shotgun);
			ContentToTypeIdDict.Add(4,	DuelCoreTypes.Gun.Submachinegun);
			ContentToTypeIdDict.Add(38,	DuelCoreTypes.Gun.TeleportationGun);
			ContentToTypeIdDict.Add(17,	DuelCoreTypes.Gun.PredatorDisk);
				
			ContentToTypeIdDict.Add(18,	DuelCoreTypes.Dec.GreenBlood);
			ContentToTypeIdDict.Add(2,	DuelCoreTypes.Dec.RedBlood);
			ContentToTypeIdDict.Add(42,	DuelCoreTypes.Dec.YellowBlood);
				
				//default:typeId = DuelCoreTypes.MapObj.Base);
		}
		
		//private static StreamReader ReadAllToMemory(StreamReader reader)
		//{
		//    char[] buffer = new char[32768];
		//    MemoryStream ms = new MemoryStream();
		//    using (var writer = new StreamWriter(ms) { AutoFlush = true })
		//    {
		//        while (true)
		//        {
		//            int read = reader.Read(buffer, 0, buffer.Length);
		//            if (read <= 0) {
		//                ms.Seek(0, SeekOrigin.Begin);
		//                return new StreamReader(ms);
		//            }
		//            writer.Write(buffer, 0, read);
		//        }
		//    }
		//}

		static public DuelMap LoadMap(DuelDomain domain, StreamReader reader)
		{
			const byte CHAR_ZERO = (byte)'0';
			
			//try {
				//byte[,,] content;
				
				var map = new DuelMap();
				
				map.Attributes.Add(DuelMapAttributes.FormatVersion, "OLD");
				
				if (reader.BaseStream is FileStream) {
					map.Attributes.Add(DuelMapAttributes.Name, Path.GetFileName(((FileStream)reader.BaseStream).Name));
					
					//reader = ReadAllToMemory(reader);
				}
				
				#if (DEBUG)
					var watch = System.Diagnostics.Stopwatch.StartNew();
				#endif
				
				//using (var reader = new BinaryReader(reader)) {
					int dimZ = reader.Read() - CHAR_ZERO;// + 1;
					int dimX = reader.Read() - CHAR_ZERO - 1;
					int dimY = reader.Read() - CHAR_ZERO - 1;
					
					//content = new byte[dimZ, dimY, dimX];
					//var groundType = GetBaseGroundEntityType();
					//var groundShape = new BoundingBoxInt(new PointInt(0, 0, -1), new PointInt(dimX, dimY, -1));
					//map.AddObject(groundType, groundShape);
					map.Box = new BoundingBoxInt(new PointInt(0, 0, 0), new PointInt(dimX, dimY, dimZ));
					
					// As odd as this seems, it's correct
					for (int z=0; z<=dimZ; z++)
					for (int x=0; x<=dimX; x++)
					for (int y=dimY; y>=0; y--) {
						var contentCode = reader.Read() - CHAR_ZERO;
						var type = ConvertContentCodeToDuelType(domain, contentCode);
						if (type != null) map.AddEntry(type, x, y, z);
						
						//only one ground object per position is needed
						if (type == null || !type.IsDescendantOf(DuelCoreTypes.Ground.Base)) {
							var ground = ConvertContentCodeToGroundType(domain, contentCode, z);
							if (ground != null) map.AddEntry(ground, x, y, z);
						}
					}
				//}

				#if (DEBUG)
					watch.Stop();
					System.Diagnostics.Debug.WriteLine(string.Format("Map R-Tree loaded in {0}ms", watch.ElapsedMilliseconds), "Duellum");
				#endif
				
				//HexGrid grid = HexGridConvert(content);
				return map;
			//} catch (IOException ex) {
			//	throw MapGrid.ThrowCantLoadMap(ex);
			//}
		}

		//internal static object GetBaseGroundEntityType()
		//{
		//    return EntityType.Get(WallTypes.Stone);
		//}
		internal static GroundType ConvertContentCodeToGroundType(DuelDomain domain, int content, int z)
		{
			if (z == 0 || content == 26) return null;
			//return (GroundObj)DuelDomain.Current.CreateObj(DuelCoreTypes.Ground.Regular);
			return (GroundType)domain.GetType(DuelCoreTypes.Ground.Regular);
		}

		internal static DuelType ConvertContentCodeToDuelType(DuelDomain domain, int content)
		{
			string typeId = null;
			//bool isDoor = false;
			
			if (!ContentToTypeIdDict.TryGetValue(content, out typeId)) typeId = DuelCoreTypes.MapObj.Base;
			
			if (typeId == null) return null;
			
			/*
			var obj = DuelDomain.Current.CreateObj(typeId);
			//if (isDoor) obj.SetValue(WallType.IsDoorProp, true);
			
			if (obj.Type.Id == DuelCoreTypes.Obj.Generic) obj.SetValue(DuelObjType.LegacyCodeProp, content);
			return obj;
			/*/
			var type = domain.GetType(typeId);
			
			if (type.Id == DuelCoreTypes.MapObj.Base) {
				DuelTypeId dynamicId = string.Concat(type.Id.ToString(), ".Dynamic_", content.ToString());
				
				//DuelType dynamicType;
				//if (!DuelDomain.Current.TryGetType(dynamicId, out dynamicType)) {
				//    dynamicType = DuelDomain.Current.GetOrCreateDynamicType(type, dynamicId);
				//    dynamicType.SetValue(DuelObjType.LegacyCodeProp, content);
				//}
				//type = dynamicType;
				type = domain.GetOrCreateDynamicType(type, dynamicId);
			}
			
			return type;
			//*/
		}
	}
}
